#include "world.h"

namespace sleek
{
    namespace core
    {
        namespace engine
        {
            void* FunctionThreadPhysics(void *data);
            void* FunctionThreadSound  (void* data);
            void* FunctionThreadRender (void* data);
            void* FunctionThreadEvent  (void* data);

            world::world(device::Device *d) : screen(d)
            {
                smgr = 0; sdmgr = 0; gui = 0; mom = 0;
                rdn = snd = phy = evt = locked = wphy = false;
                    mom = new texture::draw(screen);
                    sdmgr = new soundmanager();
                    gui = new interface(this);
                    smgr = new engine(this);
                #if defined _USE_SEMAPHORE_
                    sem_init(&mutex, 0, 1);
                #else
                    mutex = PTHREAD_MUTEX_INITIALIZER;
                    pthread_mutex_init(&mutex, 0);
                #endif
            }
            world::~world()
            {
                disableThread(etf_all);
                delete smgr;
                delete sdmgr;
                delete mom;
                #if defined _USE_SEMAPHORE_
                    sem_destroy(&mutex);
                #else
                    pthread_mutex_destroy(&mutex);
                #endif
            }
            void world::lock()
            {
                if(locked) return;
                #if defined _USE_SEMAPHORE_
                    sem_wait(&mutex);
                #else
                    pthread_mutex_lock(&mutex);
                #endif
                locked = true;
            }
            void world::unlock()
            {
                if(!locked) return;
                #if defined _USE_SEMAPHORE_
                    sem_post(&mutex);
                #else
                    pthread_mutex_unlock(&mutex);
                #endif
                locked = false;
            }
            bool world::isLock()
            {
                return locked;
            }
            void world::enableThread(int a)
            {
                lock();
                if(a & etf_sound   && !snd) { snd = true; pthread_create(&tsnd, 0, FunctionThreadSound, (void*)this); }
                if(a & etf_physics && !phy) { phy = true; pthread_create(&tphy, 0, FunctionThreadPhysics, (void*)this); }
                //if(a & etf_render  && !rdn) { rdn = true;
                if(a & etf_event   && !evt) { evt = true; pthread_create(&tevt, 0, FunctionThreadEvent, (void*)this); }
                unlock();
            }
            int world::getCurrentThread()
            {
                int a = etf_nothink;
                    if(!snd) a |= etf_sound;
                    if(!phy) a |= etf_physics;
                    if(!rdn) a |= etf_render;
                    if(!evt) a |= etf_event;
                return (e_thread_function)a;
            }
            void world::render()
            {
                if(!phy) smgr->getPhysics()->stepSimulation(1.f/45.f);
                if(!rdn) smgr->render();
                if(!rdn) gui->render();
                if(!evt) if(!gui->manage(screen->getCurrentEvent()))
                    screen->manage();
                if(!snd) sdmgr->render();
            }
            core::os::stringc world::getInitError()
            {
                if(!screen) return "No device screen support for app, crash imminent";
                if(!mom)    return "No drawing manager, can't draw element";
                if(!smgr)   return "No engine, can't use node";
                if(!gui)    return "No interface, can't use gui and cursor";
                if(!sdmgr)  return "No sound manager, can't play sound";
                return "World correctly init";
            }
            void world::printInitError()
            {
                getInitError().print();
            }
            void world::disableThread(int a)
            {
                lock();
                int result;
                if(a & etf_sound && snd){ snd = 0; pthread_join(tsnd, 0); }
                if(a & etf_render && rdn){ rdn = 0; pthread_join(tphy, 0); }
                if(a & etf_physics && phy){ phy = 0; wphy = false; }
                if(a & etf_event && evt){ evt = 0; pthread_join(tevt, 0); }
                unlock();
            }
            engine* world::getEngine()
            {
                return smgr;
            }
            interface* world::getInterface()
            {
                return gui;
            }
            soundmanager* world::getSoundManager()
            {
                return sdmgr;
            }
            texture::draw *world::getDrawManager()
            {
                return mom;
            }
            device::Device* world::getDevice()
            {
                return screen;
            }
            void world::waitPhysics()
            {
                while(wphy);
            }
            /** **************************************************** **/
            void* FunctionThreadSound(void *data)
            {
                world *smgr = (world*)data;
                try
                {
                    while(smgr->snd)
                    {
                        smgr->wphy = true;
                        smgr->sdmgr->render();
                        smgr->wphy = false;
                        os::Sleeping(10);
                    }
                }
                catch(...) { printf("Thread exception : Sound\n"); smgr->snd = 0; }
                return 0;
            }
            void* FunctionThreadPhysics(void *data)
            {
                world *smgr = (world*)data;
                try
                {
                    while(smgr->phy)
                    {
                        if(!smgr->isLock())
                            smgr->smgr->getPhysics()->stepSimulation(1.f/45.f);
                        os::Sleeping(10);
                    }
                }
                catch(...) { printf("Thread exception : Physics\n"); smgr->phy = 0; }
                return 0;
            }
            void* FunctionThreadEvent(void *data)
            {
                world *smgr = (world*)data;
                try
                {
                    while(smgr->evt)
                    {
                        smgr->gui->manage(smgr->screen->getCurrentEvent());
                        smgr->screen->manage();
                        os::Sleeping(10);
                    }
                }
                catch(...) { printf("Thread exception : Event\n"); smgr->evt = 0; }
                return 0;
            }
            void* FunctionThreadRender(void *data)
            {
                world *smgr = (world*)data;
                try
                {
                    while(smgr->rdn)
                    {
                        smgr->smgr->render();
                        smgr->gui->render();
                    }
                }
                catch(...) { printf("Thread exception : Event\n"); smgr->rdn = 0; }
                return 0;
            }
        }
    }
}
